前面花了很多篇幅在 Verilog 語法的說明和 IC 設計的概念。從這篇開始,我們要開始來接觸 RISC-V 架構。首先我們先來認識其中的 R-Type 指令。R 代表的是 Register ,也就是說 R-Type 指令主要描述的是暫存器之間的運算關係。
RISC-V 的指令都是 32 位元寬,因此接下來介紹的所有指令都需要 32 位元來表示。不同的 Type 差別在,這 32 位元中,不同的位置負責表達不同的資料,而這篇文章的目標就是了解 R-Type 中,各個位元表達的含義。
| 31 : 25 | 24 : 20 | 19 : 15 | 14 : 12 | 11 : 7 | 6 : 0 |
|---|---|---|---|---|---|
| funct7 | rs2 | rs1 | funct3 | rd | opcode |
R-Type 指令包含了 opcode, function, rd, rs1, rs2 ,以下我們詳細說明這些資料的意義。
opcode: 這個資料代表指令代表的 Type ,以 R-Type 來說,opcode 必定為 0110011 。function: 即便確認了指令屬於 R-Type ,我們仍不知道確切的功能,此時就要透過 funct7, funct3 來決定,透過 function 我們可以確定指令代表的功能為何。
為什麼 function 要被拆成兩個部分呢?因為 RISC-V 需要快速的進行指令解碼,因此所有指令的 opcode, rd, rs 都會被安排在相同位置。而剩餘的空間並不足以塞下完整的 function,所以才將他分成兩個部分。
rd: 這個資料代表目的暫存器 (destination)。當運算完畢後,結果會被存入這個暫存器。rs1, rs2: 這兩個資料都是來源暫存器 (source)。需要被運算的資料都會被存入來源暫存器。小結論:假設當前指定的運算功能為 @ ,則 R-Type 指令的含義即為 rd = rs1 @ rs2 。
以下列出所有 R-Type 指令,這些規範可以從 RISC-V 官網上找到喔!
| funct7 | rs2 | rs1 | funct3 | rd | opcode | 指令名稱 | 功能 |
|---|---|---|---|---|---|---|---|
| 0000000 | rs2 | rs1 | 000 | rd | 0110011 | ADD | rd = rs1 + rs2 |
| 0100000 | rs2 | rs1 | 000 | rd | 0110011 | SUB | rd = rs1 - rs2 |
| 0000000 | rs2 | rs1 | 001 | rd | 0110011 | SLL | rd = rs1 << rs2 |
| 0000000 | rs2 | rs1 | 010 | rd | 0110011 | SLT | rd = rs1 < rs2 |
| 0000000 | rs2 | rs1 | 011 | rd | 0110011 | SLTU | rd = rs1 < rs2 |
| 0000000 | rs2 | rs1 | 100 | rd | 0110011 | XOR | rd = rs1 ^ rs2 |
| 0000000 | rs2 | rs1 | 101 | rd | 0110011 | SRL | rd = rs1 >> rs2 |
| 0100000 | rs2 | rs1 | 101 | rd | 0110011 | SRA | rd = rs1 >>> rs2 |
| 0000000 | rs2 | rs1 | 110 | rd | 0110011 | OR | rd = rs1 | rs2 |
| 0000000 | rs2 | rs1 | 111 | rd | 0110011 | AND | rd = rs1 & rs2 |
R-Type 指令主要負責「算術邏輯運算」,其中比較特別的是「位移」和「比大小」。
位移根據方向可以分成左移 (Left) 和右移 (Right),根據性質則可以分成邏輯位移 (Logical) 和算術位移 (Arithmetic) 。雖然能得出四種組合,但是邏輯左移和算術左移的結果是相同的,因此只保留「邏輯左移」,也就是 SLL (Shift Left Logical) 。
邏輯位移指的是當一個數轉為「位元」表示時,因為位移而產生的空缺都以 0 來填補,符號上以 >> 表示邏輯右移、<< 表示邏輯左移。舉幾個例子:
5 << 2 = 20
- 因為 5 的二進位表示為
101,向左位移 2 位可以得到101xx,由於是邏輯位移,所以使用0來填補空缺得到結果10100,也就是 20- 若左移的結果超過資料能表示的最大值,則稱為 overflow ,此時超出的位元都將被省略
n << m等同於n * 2ᵐ
9 >> 3 = 1
- 因為 9 的二進位表示為
1001,向右位移 3 位可以得到xxx1,由於是邏輯位移,所以使用0來填補空缺得到結果0001,也就是 1- 特別注意的是被右移移除的位元需直接省略
n >> m等同於floor(n / 2ᵐ)
算數位移指的是當一個數轉為「位元」表示時,因為位移而產生的空缺都以 Sign Bit 來填補。如此一來,不論是正數還是負數,在位移後都能保持其正負號特性。若想知道更詳細的原因,可以參考「二補數法」。算術位移我們只討論右移,其符號為 >>> 。舉兩個例子:
9 >>> 3 = 1
- 因為 9 的二進位表示為
1001,向右位移 3 位可以得到xxx1,由於是算術位移,再加上 Sign Bit 為 0 ,所以使用0來填補空缺得到結果0001,也就是 1- 特別注意的是 Sign Bit 指的是資料的 MSB ,因為目前使用的是 RV32 ,因此要查看第 32 位元的值
-5 >>> 3 = -1
- 因為 -5 的二進位表示為
11...11111011,向右位移 3 位可以得到xxx11...11111,由於是算術位移,再加上 Sign Bit 為 1 ,所以使用1來填補空缺得到結果11111...11111,也就是 -1
SLT, SLTU 是其中較特別的指令。SLT 的全名為 Set Less Than ,而 SLTU 的全名則為 Set Less Than Unsigned ,兩者的差異是比較大小時,以「無號數」還是「有號數」。若運算結果為 rs1 < rs2 則回傳 1 ,否則為 0。
R-Type 指令是指令集中最直觀的部分,下一篇我們要接著介紹 I-Type 的指令,概念和 R-Type 相當類似。